/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.utl.ist.isr.livetrack.ui;

import java.awt.Image;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.swing.Action;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.PropertySupport.Reflection;
import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.HelpCtx;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups;
import pt.utl.ist.isr.livetrack.server.LTServer;
import pt.utl.ist.isr.livetrack.server.interfaces.Camera;
import pt.utl.ist.isr.livetrack.ui.propedit.CameraROIPropertyEditor;
import pt.utl.ist.isr.livetrack.ui.propedit.CameraZonePropertyEditor;

/**
 *
 * @author David Miguel Antunes <davidmiguel [ at ] antunes.net>
 */
public class CameraNode extends AbstractNode implements Transferable {

    public static final DataFlavor DATA_FLAVOR = new DataFlavor(CameraNode.class, "camera");
    private static Action deleteCameraAction =
            new org.openide.util.actions.SystemAction() {

                @Override
                public String getName() {
                    return NbBundle.getMessage(this.getClass(), "DeleteCamera");
                }

                @Override
                public HelpCtx getHelpCtx() {
                    return null;
                }

                @Override
                public void actionPerformed(ActionEvent ae) {
                    CameraNode cm = Utilities.actionsGlobalContext().lookup(CameraNode.class);
                    cm.server.removeCamera(cm.camera.getId());
                    CamerasNodeChildFactory factory = CamerasNodeChildFactory.getFactory(cm.server);
                    factory.refresh();
                }
            };
    private static Action openCameraFeedAction =
            new org.openide.util.actions.SystemAction() {

                @Override
                public String getName() {
                    return NbBundle.getMessage(this.getClass(), "OpenCameraFeed");
                }

                @Override
                public HelpCtx getHelpCtx() {
                    return null;
                }

                @Override
                public void actionPerformed(ActionEvent ae) {
                    CameraNode cn = Utilities.actionsGlobalContext().lookup(CameraNode.class);
                    CameraFeedTopComponent.openCameraFeed(cn.server, cn.camera, cn);
                }
            };
    private static Action resetCameraAction =
            new org.openide.util.actions.SystemAction() {

                @Override
                public String getName() {
                    return NbBundle.getMessage(this.getClass(), "ResetCamera");
                }

                @Override
                public HelpCtx getHelpCtx() {
                    return null;
                }

                @Override
                public void actionPerformed(ActionEvent ae) {
                    CameraNode cm = Utilities.actionsGlobalContext().lookup(CameraNode.class);
                    try {
                        cm.camera.getClass().getMethod("reset", new Class[0]).invoke(cm.camera);
                    } catch (IllegalAccessException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (IllegalArgumentException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (InvocationTargetException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (NoSuchMethodException ex) {
                        Exceptions.printStackTrace(ex);
                    } catch (SecurityException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            };
    private LTServer server;
    private Camera camera;
    private Sheet sheet;
    private Sheet.Set set;
    private CameraView cameraView;
    private CameraViewController cameraViewController;

    public CameraNode(LTServer server, Camera camera) {
        super(Children.LEAF, Lookups.singleton(camera));
        this.server = server;
        this.camera = camera;
        setDisplayName(camera.getId());
        init();
        cameraViewController = new CameraViewController();
        cameraView = new CameraView(server, camera, cameraViewController);
    }

    public Image getIcon(int type) {
        return ImageUtilities.loadImage("pt/utl/ist/isr/livetrack/ui/icons/camera.jpg", true);
    }

    @Override
    public Image getOpenedIcon(int type) {
        return getIcon(type);
    }

    private void init() {
        try {
            sheet = Sheet.createDefault();
            set = Sheet.createPropertiesSet();
            final Camera obj = getLookup().lookup(Camera.class);

            PropertySupport.Reflection zoneProp;
            PropertySupport.Reflection roiProp;

            addProperties(new PropertySupport.Reflection[]{
                        new PropertySupport.Reflection(obj, String.class, "getId", null),
                        new PropertySupport.Reflection(obj, String.class, "getType", null),
                        new PropertySupport.Reflection(obj, int.class, "getBinaryThreshold", "setBinaryThreshold"),
                        new PropertySupport.Reflection(obj, boolean.class, "isApplyMorphologicalClosing", "setApplyMorphologicalClosing"),
                        new PropertySupport.Reflection(obj, int.class, "getKernelHeight", "setKernelHeight"),
                        new PropertySupport.Reflection(obj, int.class, "getKernelWidth", "setKernelWidth"),
                        new PropertySupport.Reflection(obj, int.class, "getMinBlobArea", "setMinBlobArea"),
                        new PropertySupport.Reflection(obj, int.class, "getMaxBlobArea", "setMaxBlobArea"),
                        new PropertySupport.Reflection(obj, int.class, "getMinBlobActivePixels", "setMinBlobActivePixels"),
                        new PropertySupport.Reflection(obj, int.class, "getMaxBlobActivePixels", "setMaxBlobActivePixels"),
                        new PropertySupport.Reflection(obj, boolean.class, "isOptimizeClosing", "setOptimizeClosing"),
                        zoneProp = new PropertySupport.Reflection(obj, String.class, "getZoneId", "setZoneId"),
                        roiProp = new PropertySupport.Reflection(obj, Rectangle2D.class, "getRoi", "setRoi"),},
                    new String[]{
                        NbBundle.getMessage(this.getClass(), "CameraNode_id"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_class"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_binaryThreshold"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_morphologicalClosing"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_kernelHeight"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_kernelWidth"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_minBlobArea"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_maxBlobArea"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_minBlobActivePixels"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_maxBlobActivePixels"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_optimizeClosing"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_zone"),
                        NbBundle.getMessage(this.getClass(), "CameraNode_roi"),});

            if (camera.getType().equals("AxisCamera")) {
                addProperties(new PropertySupport.Reflection[]{
                            new PropertySupport.Reflection(obj, String.class, "getHostname", "setHostname"),},
                        new String[]{
                            NbBundle.getMessage(this.getClass(), "CameraNode_axis_hostname"),});
            } else if (camera.getType().equals("SequentialFileCamera")) {
                addProperties(new PropertySupport.Reflection[]{
                            new PropertySupport.Reflection(obj, String.class, "getDir", "setDir"),},
                        new String[]{
                            NbBundle.getMessage(this.getClass(), "CameraNode_file_directory"),});
            } else if (camera.getType().equals("TimestampedFileCamera")) {
                addProperties(new PropertySupport.Reflection[]{
                            new PropertySupport.Reflection(obj, String.class, "getDir", "setDir"),
                            new PropertySupport.Reflection(obj, int.class, "getFramesPerSecond", "setFramesPerSecond"),
                            new PropertySupport.Reflection(obj, boolean.class, "getMarkTimestamp", "setMarkTimestamp"),},
                        new String[]{
                            NbBundle.getMessage(this.getClass(), "CameraNode_file_directory"),
                            NbBundle.getMessage(this.getClass(), "CameraNode_frames_per_second"),
                            NbBundle.getMessage(this.getClass(), "CameraNode_MarkTimestamp"),});
            }

            zoneProp.setPropertyEditorClass(CameraZonePropertyEditor.class);
            roiProp.setPropertyEditorClass(CameraROIPropertyEditor.class);

            sheet.put(set);
        } catch (NoSuchMethodException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private void addProperties(Reflection[] props, String[] names) {
        for (int i = 0; i < props.length; i++) {
            Property property = props[i];
            property.setName(names[i]);
            set.put(property);
        }
    }

    @Override
    protected Sheet createSheet() {
        return sheet;
    }

    @Override
    public Action[] getActions(boolean context) {
        if (camera.getType().equals("AxisCamera")) {
            return new Action[]{
                        openCameraFeedAction,
                        deleteCameraAction,};
        } else if (camera.getType().equals("SequentialFileCamera")) {
            return new Action[]{
                        openCameraFeedAction,
                        deleteCameraAction,
                        resetCameraAction};
        } else if (camera.getType().equals("TimestampedFileCamera")) {
            return new Action[]{
                        openCameraFeedAction,
                        deleteCameraAction,
                        resetCameraAction};
        }
        return new Action[]{
                    openCameraFeedAction,
                    deleteCameraAction,};
    }

    @Override
    public Transferable drag() throws IOException {
        return this;
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{CameraNode.DATA_FLAVOR};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor == DATA_FLAVOR;
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if (flavor == DATA_FLAVOR) {
            return this;
        } else {
            throw new UnsupportedFlavorException(flavor);
        }
    }

    public Camera getCamera() {
        return camera;
    }

    public CameraView getCameraView() {
        return cameraView;
    }

    public CameraViewController getCameraViewController() {
        return cameraViewController;
    }

    public LTServer getServer() {
        return server;
    }
}
